package org.tiny.autounit.core.data.transport.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.tiny.autounit.core.data.transport.server.codec.NettyLengthDecoder;
import org.tiny.autounit.core.data.transport.server.codec.NettyMessageDecoder;
import org.tiny.autounit.core.data.transport.server.codec.NettyMessageEncoder;
import org.tiny.autounit.core.data.transport.server.handler.HeartBeatResponseHandler;
import org.tiny.autounit.core.data.transport.server.handler.ServerHandler;

/**
 * @author shichaoyang
 * @Description: mock服务端，用于放置mock数据，供客户端获取
 * @Note 此server可以用http服务来做，需要额外的使用tomcat来启动，为了保持core的即开即用特性，以netty server端为主
 * @date 2021-08-02 15:49
 */
@Slf4j
public class MockDataServer {


    /**
     * 服务端带参构造
     * @param serverAddress
     */
    public MockDataServer(String serverAddress) {
        this.serverAddress = serverAddress;
    }

    /**
     * 服务端绑定地址
     */
    private String serverAddress;

    /**
     * 主事件池
     */
    private EventLoopGroup bossGroup = new NioEventLoopGroup();

    /**
     * 副事件池
     */
    private EventLoopGroup workerGroup = new NioEventLoopGroup();

    /**
     * 服务端通道
     */
    private Channel serverChannel;

    /**
     * 绑定本机监听
     *
     * @throws Exception
     */
    public void bind() throws Exception {

        //启动器
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        //为Acceptor设置事件池，为客户端接收设置事件池
        serverBootstrap.group(bossGroup, workerGroup)
                //工厂模式，创建NioServerSocketChannel类对象
                .channel(NioServerSocketChannel.class)
                //等待队列大小
                .option(ChannelOption.SO_BACKLOG, 100)
                //地址复用
                .option(ChannelOption.SO_REUSEADDR, true)
                //开启Nagle算法，
                //网络好的时候：对响应要求比较高的业务，不建议开启，比如玩游戏，键盘数据，鼠标响应等，需要实时呈现；
                //            对响应比较低的业务，建议开启，可以有效减少小数据包传输。
                //网络差的时候：不建议开启，否则会导致整体效果更差。
                .option(ChannelOption.TCP_NODELAY, true)
                //日志记录组件的level
                .handler(new LoggingHandler(LogLevel.INFO))
                //各种业务处理handler
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel channel) throws Exception {
                        //空闲检测handler，用于检测通道空闲状态
                        channel.pipeline().addLast("idleStateHandler", new IdleStateHandler(45, 45, 120));
                        //拆分消息，头部字节长度为4字节
                        channel.pipeline().addLast("nettyLengthDecoder", new NettyLengthDecoder(1024 * 1024, 0, 4));
                        channel.pipeline().addLast("nettyMessageEncoder", new NettyMessageEncoder());
                        channel.pipeline().addLast("nettyMessageDecoder", new NettyMessageDecoder());
                        //心跳包业务处理，一般需要配置idleStateHandler一起使用
                        channel.pipeline().addLast("heartBeatHandler", new HeartBeatResponseHandler());
                        //业务处理handler
                        channel.pipeline().addLast("nettyHandler", new ServerHandler());
                    }
                });

        //获取ip和端口
        String[] array = serverAddress.split(":");
        String host = array[0];
        int port = Integer.parseInt(array[1]);

        //绑定端口，同步等待成功
        ChannelFuture future = serverBootstrap.bind(host, port).sync();

        //注册连接事件监听器
        future.addListener(cfl -> {
            if (cfl.isSuccess()) {
                log.info("服务端[" + host + ":" + port + "]已上线...");
                serverChannel = future.channel();
            }
        });

        //注册关闭事件监听器
        future.channel().closeFuture().addListener(cfl -> {
            //关闭服务端
            close();
            log.info("服务端[" + host + ":" + port + "]已下线...");
        });
    }

    /**
     * 关闭server
     */
    public void close() {
        //关闭套接字
        if(serverChannel!=null){
            serverChannel.close();
        }
        //关闭主线程组
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        //关闭副线程组
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
    }

}
